package me.phelps.webrtctest;

import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.os.Process;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;

import com.sinowave.ddp.Apm;
import com.sinowave.ddp.SyncQueue;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import butterknife.ButterKnife;
import butterknife.InjectView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @InjectView(R.id.toggle)
    Button toggle;
    @InjectView(R.id.et_delay)
    EditText etDelay;
    @InjectView(R.id.add_1)
    Button add1;
    @InjectView(R.id.add_5)
    Button add5;
    @InjectView(R.id.add_10)
    Button add10;
    @InjectView(R.id.min_1)
    Button min1;
    @InjectView(R.id.min_5)
    Button min5;
    @InjectView(R.id.min_10)
    Button min10;
    @InjectView(R.id.activity_main)
    LinearLayout activityMain;

    AtomicBoolean start = new AtomicBoolean(false);
    Apm apm;
    int delay;
    SyncQueue<short[]> recordQueue;
    ArrayBlockingQueue<byte[]> encodeQueue;
    ArrayBlockingQueue<byte[]> decodeQueue;
//    SyncQueue<byte[]> encodeQueue;
//    SyncQueue<byte[]> decodeQueue;
    RecordThread recordThread;
    EncoderThread encoderThread;
    DecoderThread decoderThread;
    PlayThread playThread;
    MediaFormat configFormat;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        toggle.setOnClickListener(this);
        add1.setOnClickListener(this);
        add5.setOnClickListener(this);
        add10.setOnClickListener(this);
        min1.setOnClickListener(this);
        min5.setOnClickListener(this);
        min10.setOnClickListener(this);

        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        audioManager.setMode(AudioManager.MODE_NORMAL);
//        audioManager.setSpeakerphoneOn(true);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.toggle:
                toggle();
                break;
            case R.id.add_1:
                add(1);
                break;
            case R.id.add_5:
                add(5);
                break;
            case R.id.add_10:
                add(10);
                break;
            case R.id.min_1:
                add(-1);
                break;
            case R.id.min_5:
                add(-5);
                break;
            case R.id.min_10:
                add(-10);
                break;
        }
    }

    private void add(int value){
        delay = Integer.parseInt(etDelay.getText().toString());
        delay = delay+value;
        etDelay.setText(delay+"");

    }

    private void toggle(){
        if(start.get()){
            start.set(false);
            configFormat =null;
            recordQueue =null;
            encodeQueue =null;
            decodeQueue =null;
            apm.close();

            FileHelper.instance.close();
        }else {
            FileHelper.instance.init();

            delay = Integer.parseInt(etDelay.getText().toString());
            recordQueue = new SyncQueue<>(100);
            encodeQueue = new ArrayBlockingQueue<>(100);
            decodeQueue = new ArrayBlockingQueue<>(100);
            start.set(true);
            initApm();
            recordThread = new RecordThread();
            encoderThread = new EncoderThread();
            decoderThread = new DecoderThread();
            playThread = new PlayThread();
            recordThread.start();
            encoderThread.start();
            decoderThread.start();
            playThread.start();
        }
    }

    private void initApm(){
        try {
            apm = new Apm(false,true,true,true,false,false,false);
            apm.HighPassFilter(true);
            apm.AGC(false);

            apm.AECM(true);
            apm.AECMSetSuppressionLevel(Apm.AECM_RoutingMode.LoudSpeakerphone);

            apm.NS(true);
            apm.NSSetLevel(Apm.NS_Level.VeryHigh);

            apm.AGC(true);
            apm.AGCSetAnalogLevelLimits(0, 255);
            apm.AGCSetMode(Apm.AGC_Mode.FixedDigital);
            apm.AGCSetTargetLevelDbfs(9);
            apm.AGCSetcompressionGainDb(9);
            apm.AGCEnableLimiter(true);

            apm.VAD(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class RecordThread extends Thread {

        AudioRecord record;

        public RecordThread(){
            record = AudioHelper.createAudioRecord(AudioHelper.getRecordMinBufferSize());
        }

        @Override
        public void run() {

            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
            int out_analog_level = 200;
            record.startRecording();
            while (start.get()){

                short[] shorts=null;
                try {
                    shorts = recordQueue.Producer_Get(10L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(shorts==null || shorts.length!=AudioHelper.BYTES_PER_BUF/2){///校验长度
                    shorts = AudioCodecHelper.genShorts();
                }

                int read = record.read(shorts, 0, shorts.length);
                if(read!=shorts.length){
                    continue;
                }

                //apm
                if(apm!=null){
                    apm.AGCSetStreamAnalogLevel(out_analog_level);

//                    Log.d("current delay:",""+delay);
                    apm.SetStreamDelay(delay);
                    apm.ProcessCaptureStream(shorts,0);

                    out_analog_level = apm.AGCStreamAnalogLevel();

                    if(!apm.VADHasVoice()){
                        continue;
                    }
                }

                //写初始的录音文件
                byte[] bytes = new byte[shorts.length*2];
                ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shorts);
                FileHelper.instance.write1(bytes);
                //丢入消费者队列
                recordQueue.Producer_Put(shorts);
            }

            AudioHelper.stopRecord(record);
        }
    }

    class EncoderThread extends Thread {

        MediaCodec encoder;
        ByteBuffer[] inputBuffers;
        ByteBuffer[] outputBuffers;
        MediaCodec.BufferInfo bufferInfo;

        public EncoderThread() {
            encoder = AudioCodecHelper.createEncoder(AudioCodecHelper.genFormatAAC());
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            encoder.start();

            inputBuffers = encoder.getInputBuffers();
            outputBuffers = encoder.getOutputBuffers();
            bufferInfo = new MediaCodec.BufferInfo();

            while (start.get()) {
                short[] recordData = null;
                try {
                    recordData = recordQueue.Consumer_Get(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (recordData == null || encoder == null) {
                    continue;
                }

                //input
                int inputBufferID = encoder.dequeueInputBuffer(0);
                if (inputBufferID >= 0) {
                    ByteBuffer inputBuffer = inputBuffers[inputBufferID];
                    inputBuffer.clear();

                    //要little_endian
                    byte[] byteRecordData = new byte[recordData.length*2];
                    ByteBuffer.wrap(byteRecordData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(recordData);
                    //丢入buffer
                    inputBuffer.put(byteRecordData);

                    encoder.queueInputBuffer(inputBufferID, 0, byteRecordData.length, 0, 0);
                } else {
                    //这个音频包就丢弃？
                    continue;
                }


                //output
                int outputBufferID = encoder.dequeueOutputBuffer(bufferInfo, 0);
                do {
                    if (outputBufferID >= 0) {
                        if (bufferInfo.flags != MediaCodec.BUFFER_FLAG_CODEC_CONFIG && bufferInfo.size != 0) {
                            ByteBuffer realData = encoder.getOutputBuffers()[outputBufferID];
                            realData.position(bufferInfo.offset);
                            realData.limit(bufferInfo.offset + bufferInfo.size);
                            //入decoder队
                            byte[] encodeData = new byte[bufferInfo.offset + bufferInfo.size];//长度不确定
                            realData.get(encodeData);
                            Log.d("encode:","audio byte length=" + encodeData.length+",info offset="+bufferInfo.offset+",size="+bufferInfo.size);
                            encodeQueue.offer(encodeData);
                            //写入文件
//                            FileHelper.instance.write2(encodeData);
                        }
                        encoder.releaseOutputBuffer(outputBufferID, false);
                        outputBufferID = encoder.dequeueOutputBuffer(bufferInfo, 0);
                    } else if (outputBufferID == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        outputBuffers = encoder.getOutputBuffers();
                    } else if (outputBufferID == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        Log.i("encode config:", "" + encoder.getOutputFormat().toString());
                        configFormat = encoder.getOutputFormat();//配置
                    }
                } while (outputBufferID >= 0);

                //把pcmData塞回去
                recordQueue.Consumer_Put(recordData);
            }
            AudioCodecHelper.releaseCodec(encoder);
        }
    }

    class DecoderThread extends Thread{

        MediaCodec decoder;
        ByteBuffer[] inputBuffers;
        ByteBuffer[] outputBuffers;
        MediaCodec.BufferInfo bufferInfo;

        @Override
        public void run() {

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            while (start.get() && decoder==null){
                if(configFormat==null){
                    continue;
                }
                decoder = AudioCodecHelper.createDecoder(configFormat);
                decoder.configure(configFormat,null,null,0);
                decoder.start();
            }

            inputBuffers = decoder.getInputBuffers();
            outputBuffers = decoder.getOutputBuffers();
            bufferInfo = new MediaCodec.BufferInfo();

            while (start.get()){
                byte[] encodeData = encodeQueue.poll();
                if(encodeData==null || decoder==null){
                    continue;
                }

                //input
                int inputBufferID = decoder.dequeueInputBuffer(0);
                if(inputBufferID >= 0){
                    ByteBuffer inputBuffer = inputBuffers[inputBufferID];
                    inputBuffer.clear();
                    inputBuffer.put(encodeData);

                    decoder.queueInputBuffer(inputBufferID,0,encodeData.length,0,0);
                }else {
                    continue;
                }

                //output
                int outputBufferID = decoder.dequeueOutputBuffer(bufferInfo, 0);
                do {
                    if(outputBufferID>=0){
                        if(bufferInfo.flags != MediaCodec.BUFFER_FLAG_CODEC_CONFIG && bufferInfo.size != 0){
                            ByteBuffer outputBuffer = outputBuffers[outputBufferID];
                            outputBuffer.position(bufferInfo.offset);
                            outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                            byte[] decodeData = new byte[bufferInfo.size-bufferInfo.offset];
                            outputBuffer.get(decodeData);
                            Log.d("decode:","audio length=" + decodeData.length);
                            //写到文件
                            FileHelper.instance.write3(decodeData);
                            //丢给播放队列
                            decodeQueue.offer(decodeData);
                        }
                        decoder.releaseOutputBuffer(outputBufferID,false);
                        outputBufferID = this.decoder.dequeueOutputBuffer(bufferInfo,0);
                    } else if (outputBufferID == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        //config
                        outputBuffers = this.decoder.getOutputBuffers();
                    } else if (outputBufferID == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        //config
                    }
                }while (outputBufferID>=0);
            }
            AudioCodecHelper.releaseCodec(decoder);
        }
    }


    class PlayThread extends Thread {

        AudioTrack track;
        ByteBuffer apmBuffer;
        byte[] decodeData = null;
        short[] apmData = null;
        int currentPos=0;

        public PlayThread(){
            track = AudioHelper.createAudioTrack(AudioHelper.getTrackMinBufferSize());
        }

        @Override
        public void run() {

            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
            track.play();
            apmBuffer = ByteBuffer.allocate(AudioConstants.BYTES_PER_BUF);
            apmData = new short[AudioConstants.BYTES_PER_BUF/2];

            while (start.get()){
                try {
                    decodeData = decodeQueue.poll(10,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if(decodeData==null){
                    continue;
                }else {

                    //需要裁切成10ms也就是320个字节进行apm，然后再播放
                    while (decodeData!=null){
                        if(apmBuffer.position()==apmBuffer.limit()){
                            //满了就去apm,然后播放清空
                            apmBuffer.flip();
                            apmBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(apmData);
                            apmBuffer.clear();//清空
                            if(apm!=null){
                                apm.ProcessRenderStream(apmData,0);
                            }
                            track.write(apmData,0,apmData.length);

//                            byte[] bytes = new byte[apmBuffer.limit()];
//                            apmBuffer.get(bytes);
//                            FileHelper.instance.write4(bytes);
//                            apmBuffer.clear();

                        }else {
                            //从decodeData里面取数据
                            int left4Fill = apmBuffer.remaining();//需要填充的字节数
                            int realLeft = decodeData.length-currentPos;//现有的字节数
                            if(left4Fill > realLeft){
                                //现有字节数大于需要填充的
                                apmBuffer.put(decodeData,currentPos,realLeft);//apmBuffer应该是被填满
//                                decodeQueue.Consumer_Put(decodeData);//消费完丢回队列
                                decodeData=null;
                                currentPos=0;//重置为0
                            }else {
                                //现有字节数小于需要填充的
                                apmBuffer.put(decodeData,currentPos,left4Fill);
                                currentPos += left4Fill;
                            }
                            Log.d("apm buffer fill:"," apmBuffer length="+apmBuffer.limit());
                        }
                    }
                }
                Log.d("recordQueue:","useSize="+ recordQueue.UsedSize()+",total="+ recordQueue.Total());
            }
            AudioHelper.stopTrack(track);
        }
    }
}
